// $Id: object.hpp 5 2009-09-05 10:21:39Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#ifndef XPCOM_OBJECT_HPP
#define XPCOM_OBJECT_HPP

#include <xpcom/config.hpp>
#include <cstddef>
#include <boost/mpl/and.hpp>
#include <boost/mpl/begin_end.hpp>
#include <boost/mpl/copy_if.hpp>
#include <boost/mpl/deref.hpp>
#include <boost/mpl/empty.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/inherit.hpp>
#include <boost/mpl/inherit_linearly.hpp>
#include <boost/mpl/front.hpp>
#include <boost/mpl/next.hpp>
#include <boost/mpl/not.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/push_front.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/aux_/na.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_params_with_a_default.hpp>
#include <boost/type_traits/add_pointer.hpp>
#include <boost/type_traits/is_base_and_derived.hpp>
#include <boost/type_traits/is_same.hpp>
#include <xpcom/assert.hpp>
#include <xpcom/interface.hpp>
#include <xpcom/interface_table.hpp>
#include <xpcom/memory.hpp>
#include <xpcom/ptr.hpp>
#include <xpcom/result_code.hpp>
#include <xpcom/impl/interface_impl.hpp>
#include <xpcom/detail/atomic_count.hpp>

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_PREFIX
#endif

#ifndef XPCOM_LIMIT_OBJECT_INTERFACE_SIZE
#  define XPCOM_LIMIT_OBJECT_INTERFACE_SIZE BOOST_MPL_LIMIT_VECTOR_SIZE
#endif

#if XPCOM_LIMIT_OBJECT_INTERFACE_SIZE < BOOST_MPL_LIMIT_VECTOR_SIZE
#  define XPCOM_OBJECT_INTERFACE_SIZE XPCOM_LIMIT_OBJECT_INTERFACE_SIZE
#else
#  define XPCOM_OBJECT_INTERFACE_SIZE BOOST_MPL_LIMIT_VECTOR_SIZE
#endif

#define XPCOM_OBJECT_ENUM_PARAMS \
    BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT( \
        XPCOM_OBJECT_INTERFACE_SIZE, typename T, boost::mpl::na)

#define XPCOM_OBJECT_ENUM_PARAMS_SEQ \
    typename boost::mpl::vector< \
        BOOST_PP_ENUM_PARAMS(XPCOM_OBJECT_INTERFACE_SIZE, T)>::type

namespace xpcom {

//
// Thread model tags.
//
namespace thread_model {

struct single_threaded_tag {};
struct multi_threaded_tag {};
struct main_threaded_tag : single_threaded_tag {};

#ifdef BOOST_HAS_THREADS
    typedef multi_threaded_tag default_threaded_tag;
#else
    typedef single_threaded_tag default_threaded_tag;
#endif

} // namespace xpcom::thread_model

//
// Factory model tags.
//
namespace factory_model {

struct void_factory_tag {};
struct standard_factory_tag {};
struct singleton_factory_tag {};

} // namespace xpcom::factory_model

//
// Custom interfaces are classes which embedded into object_base's inheritance
// list, but implements query_interface them self instead of using default
// interface table.  Examples are aggregation, tear-off and conditional
// interfaces.
//
// All custom interface (which will not put into interface table) must inherit
// from custom_interface_tag.  So that we can distinguish them from normal
// interfaces (imagine that custom interface can also be derived from some
// XPCOM interface and is convertible to xpcom::interface*).
//
struct custom_interface_tag
{
    // Must have function of signature with Class to be the derived object,
    // which returns true if query succeeded, and false otherwise:
    //
    // template <typename Class>
    // result_t query(Class* pthis, const uuid& iid, void** obj);
    //
    // or non-templated version (which ignores pthis usually):
    //
    // result_t query(void*, const uuid& iid, void** obj);
};

// Need forward declaration here
template <typename Class, typename Seq>
class object_base;

namespace detail
{

template <typename T>
struct is_custom_interface :
    boost::is_base_and_derived<custom_interface_tag, T>
{
};

// Auxiliary class for providing types for object.
template <typename Class, typename Seq>
struct object_aux
{
    // All interfaces
    typedef typename impl::interface_impl_sequence<Class, Seq>::type
        all_interfaces_type;

    // Types suitable to be used by interface table
    typedef typename boost::mpl::copy_if<
        all_interfaces_type,
        boost::mpl::and_<
            boost::mpl::not_<is_custom_interface<boost::mpl::_1> >,
            is_itable_interface<boost::mpl::_1>
        >
    >::type interfaces_type;

    // Custom interfaces
    typedef typename boost::mpl::copy_if<
        all_interfaces_type,
        is_custom_interface<boost::mpl::_1>
    >::type custom_interfaces_type;

    // Normalized interface sequence by add <interface> if is empty
    typedef typename boost::mpl::eval_if<
        boost::mpl::empty<interfaces_type>,
        boost::mpl::vector1<
            typename impl::interface_impl_type<Class, interface>::type
        >,
        interfaces_type
    >::type normalized_interfaces_type;

    // Normalized all interface sequence by add <interface> to the front if
    // interfaces_type is empty
    typedef typename boost::mpl::eval_if<
        boost::mpl::empty<interfaces_type>,
        boost::mpl::push_front<
            all_interfaces_type,
            typename impl::interface_impl_type<Class, interface>::type
        >,
        all_interfaces_type
    >::type normalized_all_interfaces_type;
};

// Make query for all the custom interfaces of the object.
template <bool done = true>
struct custom_interface_query_impl
{
    template <
        typename Iterator,
        typename LastIterator,
        typename Class,
        typename Seq
    >
    static result_t execute(
            Iterator*, LastIterator*,
            object_base<Class, Seq>&, const uuid&, void** obj)
    {
        *obj = 0;
        return NS_ERROR_NO_INTERFACE;
    }
};

template <>
struct custom_interface_query_impl<false>
{
    template <
        typename Iterator,
        typename LastIterator,
        typename Class,
        typename Seq
    >
    static result_t execute(
            Iterator*, LastIterator*,
            object_base<Class, Seq>& o, const uuid& iid, void** obj)
    {
        typedef typename boost::mpl::deref<Iterator>::type item;

        result r = static_cast<item&>(o).query(
                static_cast<Class*>(&o), iid, obj);
        if (r.succeeded())
            return r;

        typedef typename boost::mpl::next<Iterator>::type iter;
        return custom_interface_query_impl<
                boost::is_same<iter, LastIterator>::value
            >::execute((iter*)0, (LastIterator*)0, o, iid, obj);
    }
};

template <typename Class, typename Seq>
inline result_t custom_interface_query(
        object_base<Class, Seq>& o, const uuid& iid, void** obj)
{
    typedef typename object_aux<Class, Seq>::custom_interfaces_type sequence;

    typedef typename boost::mpl::begin<sequence>::type first;
    typedef typename boost::mpl::end<sequence>::type last;

    return custom_interface_query_impl<
            boost::is_same<first, last>::value
        >::execute((first*)0, (last*)0, o, iid, obj);
}

} // namespace xpcom::detail

//
// Base XPCOM object implementations which implement query_interface.
//
template <typename Class, typename Seq>
class XPCOM_NO_VTABLE object_base :
    public boost::mpl::inherit_linearly<
        typename detail::object_aux<Class, Seq>::normalized_all_interfaces_type,
        boost::mpl::inherit<boost::mpl::_1, boost::mpl::_2> >::type
{
    typedef object_base<Class, Seq> this_type;
    typedef detail::object_aux<Class, Seq> aux_type;

    typedef typename aux_type::normalized_interfaces_type interfaces_type;
    typedef typename aux_type::custom_interfaces_type custom_interfaces_type;

    typedef interface_table<this_type, interfaces_type> interface_table_type;

public:
    // Cast to interface* helper
    noref_ptr<interface> upcast_to_interface()
    {
        typedef typename boost::mpl::front<
            interfaces_type>::type first_interface_type;

        return static_cast<interface*>(
                static_cast<first_interface_type*>(this));
    }

#ifdef XPCOM_ENABLE_OBJECT_OPERATOR_NEW_DELETE
    static void* operator new(std::size_t n)
    {
        return memory::alloc_data(n);
    }

    static void operator delete(void* p, std::size_t /*n*/)
    {
        memory::free_data(p);
    }
#endif // XPCOM_ENABLE_OBJECT_OPERATOR_NEW_DELETE

    XPCOM_IMETHODIMP query_interface(const uuid& iid, void** obj)
    {
        XPCOM_ASSERT(obj);
        if (XPCOM_UNLIKELY(!obj))
            return NS_ERROR_NULL_POINTER;

        typedef typename boost::mpl::empty<custom_interfaces_type>::type tag;
        return query_interface_dispatch(iid, obj, tag());
    }

protected:
    virtual ~object_base()
    {
    }

private:
    // Hide type, impl_type typedefs from interface_impl
    typedef void* type;
    typedef void* impl_type;

    // Hide the access of functions provided by base classes
    void _object();
    void query();

    // query_interface implementations based on whether have custom interfaces
    result_t query_interface_dispatch(
            const uuid& iid, void** obj, boost::mpl::true_)
    {
        result r = interface_table_query(
                static_cast<void*>(this),
                interface_table_type::entries, iid, obj);
        if (r.succeeded())
            return r;

        return detail::custom_interface_query(*this, iid, obj);
    }

    result_t query_interface_dispatch(
            const uuid& iid, void** obj, boost::mpl::false_)
    {
        return interface_table_query(
                static_cast<void*>(this),
                interface_table_type::entries, iid, obj);
    }
};

//
// Basic XPCOM object implementation.
//
template <typename Class, XPCOM_OBJECT_ENUM_PARAMS>
class XPCOM_NO_VTABLE object :
    public object_base<Class, XPCOM_OBJECT_ENUM_PARAMS_SEQ>
{
public:
    typedef thread_model::default_threaded_tag thread_model_tag;
    typedef factory_model::standard_factory_tag factory_model_tag;

    object() : refcnt_(0)
    {
    }

    XPCOM_IMETHODIMP_(refcnt_t) add_ref()
    {
        return static_cast<refcnt_t>(++refcnt_);
    }

    XPCOM_IMETHODIMP_(refcnt_t) release()
    {
        long n = --refcnt_;
        if (n == 0) {
#ifdef XPCOM_ENABLE_OBJECT_TRY_CATCH_DELETION
            try {
                delete this;
            } catch (...) {
                // ignore
            }
#else // XPCOM_ENABLE_OBJECT_TRY_CATCH_DELETION
            delete this;
#endif // XPCOM_ENABLE_OBJECT_TRY_CATCH_DELETION
            return 0;
        }
        XPCOM_ASSERT(n > 0);
        return static_cast<refcnt_t>(n);
    }

private:
    detail::atomic_count refcnt_;

    object(const object&);
    const object& operator=(const object&);
};

//
// XPCOM object implementation that is not thread safe.
//
template <typename Class, XPCOM_OBJECT_ENUM_PARAMS>
class XPCOM_NO_VTABLE single_threaded_object :
    public object_base<Class, XPCOM_OBJECT_ENUM_PARAMS_SEQ>
{
public:
    typedef thread_model::single_threaded_tag thread_model_tag;
    typedef factory_model::standard_factory_tag factory_model_tag;

    single_threaded_object() : refcnt_(0)
    {
    }

    XPCOM_IMETHODIMP_(refcnt_t) add_ref()
    {
        return ++refcnt_;
    }

    XPCOM_IMETHODIMP_(refcnt_t) release()
    {
        XPCOM_ASSERT(refcnt_ > 0);
        --refcnt_;
        if (refcnt_ == 0) {
            refcnt_ = 1; // stabilize
#ifdef XPCOM_ENABLE_OBJECT_TRY_CATCH_DELETION
            try {
                delete this;
            } catch (...) {
                // ignore
            }
#else // XPCOM_ENABLE_OBJECT_TRY_CATCH_DELETION
            delete this;
#endif // XPCOM_ENABLE_OBJECT_TRY_CATCH_DELETION
            return 0;
        }
        return refcnt_;
    }

private:
    refcnt_t refcnt_;

    single_threaded_object(const single_threaded_object&);
    const single_threaded_object& operator=(const single_threaded_object&);
};

//
// XPCOM object implementation without reference counter.
//
template <typename Class, XPCOM_OBJECT_ENUM_PARAMS>
class XPCOM_NO_VTABLE static_object :
    public object_base<Class, XPCOM_OBJECT_ENUM_PARAMS_SEQ>
{
public:
    typedef thread_model::default_threaded_tag thread_model_tag;
    typedef factory_model::void_factory_tag factory_model_tag;

    static_object()
    {
    }

    XPCOM_IMETHODIMP_(refcnt_t) add_ref()
    {
        return 2;
    }

    XPCOM_IMETHODIMP_(refcnt_t) release()
    {
        return 1;
    }
};

//
// XPCOM object implementation that embedded in another object.
// embedded_object can not be used as base object class of aggregatable.
//
template <typename Class, typename Container, XPCOM_OBJECT_ENUM_PARAMS>
class XPCOM_NO_VTABLE embedded_object :
    public object_base<Class, XPCOM_OBJECT_ENUM_PARAMS_SEQ>
{
protected:
    typedef Container container_type;
public:
    typedef typename container_type::thread_model_tag thread_model_tag;
    typedef factory_model::void_factory_tag factory_model_tag;

    explicit embedded_object(container_type* container) :
        container_(container)
    {
    }

    XPCOM_IMETHODIMP_(refcnt_t) add_ref()
    {
        XPCOM_ASSERT(container_);
        return container_->add_ref();
    }

    XPCOM_IMETHODIMP_(refcnt_t) release()
    {
        XPCOM_ASSERT(container_);
        return container_->release();
    }

protected:
    container_type* container() const
    {
        return container_;
    }

private:
    container_type* container_;
};

//
// Inherited XPCOM object implementation.
//
// This class is used when you're going to inherit from something that already
// implements some interfaces, but also implements additional interfaces.
// In this case the interface methods are forwarded to the appropriate base.
//
template <typename Class, typename Base, XPCOM_OBJECT_ENUM_PARAMS>
class XPCOM_NO_VTABLE inherited_object :
    public Base,
    public object_base<Class, XPCOM_OBJECT_ENUM_PARAMS_SEQ>
{
    typedef Base base_type;
    typedef object_base<Class, XPCOM_OBJECT_ENUM_PARAMS_SEQ> object_base_type;
public:
    typedef typename base_type::thread_model_tag thread_model_tag;
    typedef typename base_type::factory_model_tag factory_model_tag;

    inherited_object()
    {
    }

    noref_ptr<interface> upcast_to_interface()
    {
        return object_base_type::upcast_to_interface();
    }

#ifdef XPCOM_ENABLE_OBJECT_OPERATOR_NEW_DELETE
    static void* operator new(std::size_t n)
    {
        return base_type::operator new(n);
    }

    static void operator delete(void* p, std::size_t n)
    {
        base_type::operator delete(p, n);
    }
#endif // XPCOM_ENABLE_OBJECT_OPERATOR_NEW_DELETE

    XPCOM_IMETHODIMP query_interface(const uuid& iid, void** obj)
    {
        result r = object_base_type::query_interface(iid, obj);
        if (r.succeeded())
            return r;

        return base_type::query_interface(iid, obj);
    }

    XPCOM_IMETHODIMP_(refcnt_t) add_ref()
    {
        return base_type::add_ref();
    }

    XPCOM_IMETHODIMP_(refcnt_t) release()
    {
        return base_type::release();
    }

private:
    inherited_object(const inherited_object&);
    const inherited_object& operator=(const inherited_object&);
};

//
// Up-cast an object pointer to interface pointer, can be overloaded.
// This is used in module implementation.
//
template <typename T>
inline noref_ptr<interface> upcast_to_interface(T* p)
{
    return p->upcast_to_interface();
}

} // namespace xpcom

#undef XPCOM_OBJECT_ENUM_PARAMS_SEQ
#undef XPCOM_OBJECT_ENUM_PARAMS
#undef XPCOM_OBJECT_INTERFACE_SIZE

#ifdef BOOST_HAS_ABI_HEADERS
#include BOOST_ABI_SUFFIX
#endif

#endif // XPCOM_OBJECT_HPP
